package com.aspire.boc.mms.gwcmp.util;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.web.servlet.support.RequestContext;

import com.aspire.boc.mms.gwcmp.util.enums.CapClassType;
import com.aspire.boc.mms.gwcmp.util.enums.CapParamName;

/**
 * 网关配置校验工具类
 * @author zhanghao
 *
 */
public class CapChecker {

	private static final Log logger = LogFactory.getLog(CapChecker.class);
	
	private static final String STRICT_SEPERATOR = "#";
	
	private static final Map<String, Map<String, Pattern>> regexMap = new LinkedHashMap<String, Map<String, Pattern>>();
	
	/**
	 *  cap校验模板文件名
	 */
	private static final String STRICT_FILENAME = "cap-param-strict.json";
	
	static {
		
		init();
	}
	
	/**
	 * 检查CAP业务及启动参数
	 * @param requestContext spring包装的请求对象
	 * @param fullClassName 全路径类名.
	 * 					例：com.aspire.nm.capproxy.proxy.cap.capa.sms_http_simple_server.Server
	 * @param paramName 参数名。
	 * 				    例：ParamName.CAP_BUSINESS
	 * @param paramValueStr 待检查参数串.
	 * 					例：black_check=1,white_check=0,keyword_check=1
	 * @return 检查结果对象 {@link CheckResult} 
	 */
	public static CheckResult checkParams(RequestContext requestContext, String fullClassName, CapParamName paramName, String paramValueStr) {
		
		if(requestContext == null) {
			logger.error("param is empty: requestContext["+requestContext+"]");
			return new CheckResult(false, "common.error.paramError");
		}
		if(StringUtils.isBlank(paramValueStr)) {
			return new CheckResult(true);
		}
		if(StringUtils.isBlank(fullClassName) || paramName == null || !paramValueStr.contains("=")) {
			logger.error("param is empty: fullClassName["+fullClassName+"], paramName["+paramName+"] or paramValueStr["+paramValueStr+"]");
			return new CheckResult(false, requestContext.getMessage("common.error.paramError"));
		}
		
		Map<String, String> paramMap = convertPropertiesString2Map(paramValueStr);
		
		String capAKey = CapClassType.CAP_ACCESS_CLASS.getVal() + 
				STRICT_SEPERATOR + fullClassName + 
				STRICT_SEPERATOR + paramName.getVal();
		String capPKey = CapClassType.CAP_PROCESS_CLASS.getVal() + 
				STRICT_SEPERATOR + fullClassName + 
				STRICT_SEPERATOR + paramName.getVal();
		String fullKey = regexMap.containsKey(capAKey) ? capAKey :
									(regexMap.containsKey(capPKey) ? capPKey : null);
		if(fullKey == null || !regexMap.containsKey(fullKey)) {
			logger.error("fail to check params: fullClassName["+fullClassName+"], paramName["+paramName+"],paramValueStr["+paramValueStr+"], "
					+ "regex regular not exists");
			return new CheckResult(false, requestContext.getMessage("common.error.paramError"));
		}
		Map<String, Pattern> regexItemMap = regexMap.get(fullKey);
		if(regexItemMap.isEmpty())
			return new CheckResult(true);
		Iterator<String> regexItemKeysIt = regexItemMap.keySet().iterator();
		boolean isOk = true;
		List<String> errorMessges = new ArrayList<String>();
		while(regexItemKeysIt.hasNext()) {
			String regexItemKey = regexItemKeysIt.next();
			Pattern regexItemPattern = regexItemMap.get(regexItemKey);
			String paramValue = paramMap.get(regexItemKey);
			if(!regexItemPattern.matcher(StringUtils.trimToEmpty(paramValue)).matches()) {
				isOk = false;
				errorMessges.add(requestContext.getMessage("common.error.cap.paramCheckFailure", 
						new String[]{paramName.getVal() + "." + regexItemKey, paramValue, regexItemPattern.toString()}));
			}
				
		}
		CheckResult checkResult = new CheckResult(isOk);
		if(!isOk) {
			checkResult.setErrorMsgList(errorMessges);
		}
		return checkResult;
	}
	
	/**
	 * 读取配置文件，转化为json对象，初始化正则表达式
	 */
	private static void init() {
		
		URL resource = CapChecker.class.getClassLoader().getResource(STRICT_FILENAME);
		if(resource == null) {
			throw new RuntimeException("fail to init capChecker, cap config file[capParamStrict.json] not exists!");
		}
		String cfgFilePath = resource.getPath();
		try {
			logger.info("begin to read cap check config file in path: " + cfgFilePath);
			String cfgStr = FileUtils.readFileToString(new File(cfgFilePath));
			logger.info("begin to convert cap check config string to json object, cfgtStr: ["+cfgStr+"]");
			JSONObject cfgJson = new JSONObject(cfgStr);
			assembleRegexMap(cfgJson, null, regexMap);
			logger.info("success to convert cap check config string to json object: "+cfgJson+", \r\n assembled regex map: "+regexMap);
		} catch (IOException e) {
			logger.error("fail to load cap config file in path : " + cfgFilePath, e);
		} catch (JSONException e) {
			logger.error("fail to convert  cap config file to json in path : " + cfgFilePath, e);
		}
	}
	
	/**
	 * 递归组装正则表达式集合
	 * @param cfgJson
	 * @param cfgKeySerial
	 * @param regexMap
	 * @throws JSONException
	 */
	@SuppressWarnings("unchecked")
	private static void assembleRegexMap(JSONObject cfgJson, StringBuffer cfgKeySerial, Map<String, Map<String, Pattern>> regexMap) throws JSONException {
		
		if(cfgJson.length()>0) {
			Iterator<String> cfgKeys = cfgJson.keys();
			if(cfgKeySerial == null) {
				cfgKeySerial = new StringBuffer();
			}
			while(cfgKeys.hasNext()) {
				String cfgKey = cfgKeys.next();
				Object nextObj = cfgJson.get(cfgKey);
				if(!(nextObj instanceof JSONObject)) 
					continue;
				JSONObject nextJson = (JSONObject)nextObj;
				if(nextJson.length() == 0) {
					continue;
				}
				Iterator<String> nextCfgKeys = nextJson.keys();
				boolean isNextJsonLeaf = true;
				Map<String, Pattern> tmpRegexMap = new LinkedHashMap<String, Pattern>();
				while(nextCfgKeys.hasNext()) {
					String nextCfgKey= nextCfgKeys.next();
					Object nextCfg = nextJson.get(nextCfgKey);
					if(!(nextCfg instanceof String)) {
						isNextJsonLeaf = false;
						continue;
					}
					tmpRegexMap.put(nextCfgKey, Pattern.compile((String)nextCfg));
				}
				if(!isNextJsonLeaf) {
					StringBuffer newCfgKeySerial = new StringBuffer(cfgKeySerial.toString());
					if(newCfgKeySerial.length() == 0) {
						newCfgKeySerial.append(cfgKey);
					} else {
						newCfgKeySerial.append(STRICT_SEPERATOR).append(cfgKey);
					}
					assembleRegexMap(nextJson, newCfgKeySerial, regexMap);
				} else {
					StringBuffer newCfgKeySerial = new StringBuffer(cfgKeySerial.toString());
					if(newCfgKeySerial.length() == 0) {
						newCfgKeySerial.append(cfgKey);
					} else {
						newCfgKeySerial.append(STRICT_SEPERATOR).append(cfgKey);
					}
					regexMap.put(newCfgKeySerial.toString(), tmpRegexMap);
				}
			}
		}
	}
	
	/**
	 * 转换参数字符串为map
	 * @param paramValue
	 * @return
	 */
	private static Map<String, String> convertPropertiesString2Map(String paramValue) {
		
		if(StringUtils.isBlank(paramValue) || !paramValue.contains("="))
			return null;
		String[] params = paramValue.split(",");
		int len = params.length;
		Map<String, String> map = new HashMap<String, String>(len);
		for(int i=0; i<len; i++) {
			String[] paramItems = params[i].split("=");
			if(paramItems.length!=2) 
				continue;
			String itemKey = StringUtils.trim(paramItems[0]);
			String itemVal = StringUtils.trim(paramItems[1]);
			if(itemKey == null || itemVal == null)
				continue;
			map.put(itemKey, itemVal);
		}
		return map;
	}
	
}
